'use strict';
import { isDOM } from '../utils/is-dom';
import { isWindow } from '../utils/is-window';
import DefaultEventEmiter from '../utils/native-event-emiter';

/** 
 * 事件模型
 * @constructor Model
 **/
class EventModel {
	/**
	 * @param {object} event 原生JS事件对象
	 * @param {*} data 传递给事件模型的数据  
	 * @desc IE模型中event是一个全局唯一的对象绑定在window对象上
	 * @desc 这里使用对象的自身属性和方法
	 */
	constructor(event, data = null) {
		this.originalEvent = event ? event : window.event; // 原生事件模型 
		this.type = this.originalEvent.type; // 事件对象类型
		this.target = this.originalEvent.target || this.originalEvent.srcElement; // 事件发生目标
		this.timestamp = this.originalEvent.timeStamp || Date.now(); // 事件发生时间
		this.emited = Boolean(this.originalEvent.emited); // 事件是否自动触发 
		this.data = data; // 事件模型携带的数据
	}

	/** 
	 * 阻止默认事件
	 * 
	 * @memberOf 
	 **/
	preventDefault() {
		const evt = this.originalEvent;
		if (evt.preventDefault) {
			evt.preventDefault();
		} else {
			evt.returnValue = false;
		}
	}

	/** 
	 * 阻止冒泡
	 * 
	 * @memberOf
	 **/
	stopPropagation() {
		const evt = this.originalEvent;
		if (evt.stopPropagation) {
			evt.stopPropagation();
		} else {
			evt.cancelBubble = true;
		}
	}

	/**
	 * 阻止同类监听器
	 * 阻止事件冒泡，如果几个监听器被绑定到同一个元素的相同事件上
	 * 则按照它们被添加的顺序调用它们，
	 * 如果有一个调用期间调用了event.stopImmediatePropagation()，则不会调用剩余的监听器。
	 * 
	 * @memberOf
	 **/
	stopImmediatePropagation() {
		const evt = this.originalEvent;
		if (evt.stopImmediatePropagation) {
			evt.stopImmediatePropagation();
		} else {
			evt.cancelBubble = true;
		}
	}
}

/** 
 * 事件管理器 
 * 
 * -------------------------------------------- 实例属性、方法
 * @method getMaxListeners() ✅ 获取当前事件的最大监听器数量
 * @method setMaxListeners(n) ✅ 设置当前事件的最大监听器数量
 * @method eventNames() ✅ 获取实例绑定的事件处理目标绑定的事件名称的集合[Array]
 * @method listeners(eventName) ✅ 根据传入的事件名称获取其绑定的监听器数量，传入的事件不存在返回0，事件名称错误时抛出一个异常
 * @method on(eventName,listener,[options]) ✅ 绑定事件监听器，参数0：事件名称，参数1：事件监听器函数，参数2：是一个对象
 * @method once(eventName,listener,[options]) ✅ 绑定一次性事件监听器，参数同 on, 在on的基础上实现的
 * @method off([eventName],[listener]) ✅ 解绑事件监听器，当不传递参数时，会默认解绑所有监听器
 * @method emit(eventName,[...args]) ✅ 主动触发指定的事件，多余的参数将传递给处理函数，可以使用 e.data来访问，同时，e.emited变为true，表示当前事件为主动触发
 * @method prep(eventName,listener,[options]) ✅ 前置事件监听器，在 on 的基础上实现，参数同 on
 * 
 * -------------------------------------------- 静态属性、方法
 * @default defaultMaxListeners ✅
 * @default addEventListener ✅ DOM绑定事件
 * @default removeEventListener ✅ DOM删除事件
 * @default emitEventListener ✅ DOM触发事件或伪造事件模型
 **/
export class EventEmiter {
	constructor(element) {
		if (!isDOM(element) && !isWindow(element)) {
			throw new TypeError('[Event Error] This object must be an HTML element.');
		}

		// 事件绑定目标
		this._target = element;

		// 默认事件监听器的最大个数
		this._maxListeners = EventEmiter.defaultMaxListeners;

		// 事件队列 
		this._queue = {};
	}

	/** 
	 * 获取事件监听的最大个数 
	 **/
	getMaxListeners() {
		return this._maxListeners;
	}

	/**
	 * 默认情况下，如果为特定事件添加了超过 10 个监听器，则 EventEmitter 会打印一个警告。
	 * 此限制有助于寻找内存泄露
	 * 
	 * @param {number} n 
	 */
	setMaxListeners(n) {
		this._maxListeners = n < 0 ? EventEmiter.defaultMaxListeners : Math.floor(n);
	}

	/** 
	 *
	 * @description 返回一个列出触发器已注册监听器的事件的数组, 数组中的值为字符串或符号。
	 **/
	eventNames() {
		return Object.keys(this._queue);
	}

	/**
	 * 获取指定数据的监听器的数量
	 * @param {string} eventName 
	 */
	listeners(eventName) {
		if ('string' === typeof eventName) {
			if (Reflect.has(eventName)) {
				return this._queue[eventName].length;
			} else {
				return 0
			}
		}

		throw new TypeError('参数类型错误，事件名称应该是字符串类型');
	}

	/**
	 * 绑定事件监听
	 *
	 * @param {string}   eventName 事件名称
	 * @param {function} listener 事件处理函数
	 * @param {object}   options 传递给事件对象的配置
	 * @param {boolean}  options.once 一次性监听，由于目前原生 addEventListener 的 'once' 参数存在兼容性问题，
	 * 								 因此采用绑定一次，删除一次的方式来实现
	 * @param {boolean}  options.capture 事件捕获
	 * @param {boolean}  options.passive 是否无效preventDefault
	 * @param {boolean}  options.once 是否触发一次
	 * @param {boolean}  options.prep 是否为前置触发器
	 */
	on(eventName, listener, { data = null, capture = false, passive = false, once = false, prep = false } = options) {

		if ('string' !== typeof eventName) {
			throw new TypeError('The event name must be a string.'); // 确保 eventName 是字符串
		}
		if ('function' !== typeof listener) {
			throw new TypeError('The event listener must be a function.'); // 确保 listener 是函数
		}
		if ('object' !== typeof options) {
			throw new TypeError('The options must be an object.'); // 确保 options 是对象
		}

		this._queue[eventName] = this._queue[eventName] || [];

		const refer = this._target;
		const queue = this._queue[eventName];
		let actual, index;

		if (listener.refer === refer && listener.timestamp) {
			actual = listener;
		} else {
			// 实际的事件监听器 
			actual = function (event) {
				const realEvent = new EventModel(event, actual.data);
				actual.listener.call(actual.refer, realEvent);
				if (actual.once === true) {
					EventEmiter.removeEventListener(actual.refer, actual.eventName, actual, {
						passive: actual.passive,
						capture: actual.capture
					});
					actual.queue.splice(actual.queue.indexOf(actual), 1); // 从当前事件队列里踢出
				}
			};

			actual.eventName = eventName;
			actual.refer = refer;
			actual.listener = listener;
			actual.data = data;
			actual.passive = passive;
			actual.capture = capture;
			actual.once = once;
			actual.timestamp = Date.now();
			actual.queue = queue;
		}

		if (prep) {
			// 加长数组
			queue.unshift(actual);
			index = 0;
		} else {
			queue.push(actual);
			index = queue.length - 1;
		}

		EventEmiter.addEventListener(refer, eventName, actual, { capture, passive });

		if (queue.length >= this._maxListeners && this._maxListeners !== 0 && this._maxListeners !== Infinity) {
			console.warn('[EventEmiter Warn]', `事件'${eventName}'的监听器数量已超过${this._maxListeners}，可能会造成内存溢出等异常`)
		}
		return this;
	};

	/**
	 * 前置事件监听程序
	 * @param {string} eventName 事件名称
	 * @param {function} listener 监听程序 
	 * @desc 参数同 on 
	 */
	prep(eventName, listener, { data = null, capture = false, passive = false, once = false }) {
		let queue = this._queue[eventName] || [];
		let temps = [];

		for (let index = 0; index < queue.length; index++) {
			const actual = queue[index];

			temps[index] = actual;
			EventEmiter.removeEventListener(actual.refer, actual.eventName, actual, {
				capture: actual.capture,
				passive: actual.passive
			});

			queue.splice(index, 1);
		}

		if (queue.length !== 0) { // 保证原始队列清空
			queue = [];
			this._queue[eventName] = queue;
		}

		this.on(eventName, listener, { data, once, capture, passive, prep: true });

		for (let index = 0; index < temps.length; index++) {
			const actual = queue[index];

			this.on(actual.eventName, actual, {
				data: actual.data,
				once: actual.once,
				capture: actual.capture,
				passive: actual.passive,
				prep: false
			});
		}

		temps = null;

		return this;
	}

	/**
	 * 绑定执行一次的事件监听
	 *
	 * @param {string} eventName 事件名称
	 * @param {function} listener 事件处理函数
	 * @param {object} options 传递给事件模型的数据
	 **/
	once(eventName, listener, { data = null, capture = false, passive = false }) {

		this.on(eventName, listener, { data, capture, passive, once: true });
		return this;
	};

	/**
	 * 移除指定的事件监听
	 *
	 * @param {string} eventName 事件名称
	 * @param {function} listener 需要移除的事件处理函数的名称
	 **/
	off(eventName, listener) {

		const refer = this._target;

		if ('string' === typeof eventName) {
			const queue = this._queue[eventName] || [];
			const hasListener = 'function' === typeof listener;

			for (let index = 0; index < queue.length; index++) {
				const actual = queue[index];
				if (!hasListener) {
					EventEmiter.removeEventListener(refer, eventName, actual, {
						capture: actual.capture,
						passive: actual.passive
					});
				} else {
					if (listener === actual.listener) {
						EventEmiter.removeEventListener(refer, eventName, actual, {
							capture: actual.capture,
							passive: actual.passive
						});
						queue.splice(index, 1);
						break;
					}
				}
			}

			if (!hasListener) {
				queue.length = 0;
				Reflect.deleteProperty(this._queue, eventName);
			}

		} else if (!eventName) {
			// 一个参数都没提供, 直接清空 queue
			for (let name in this._queue) {
				const queue = this._queue[name];
				for (let index = 0; index < queue.length; index++) {
					const actual = queue[index];
					EventEmiter.removeEventListener(refer, name, actual, {
						capture: actual.capture,
						passive: actual.passive
					});
				}
				queue.length = 0;
			}
			this._queue = {};
		}

		return this;
	};

	/**
	 * 主动触发指定的事件监听
	 * 按监听器的注册顺序，同步地调用每个注册到名为 eventName 事件的监听器，并传入提供的参数。
	 * 如果事件有监听器，则返回 true ，否则返回 false。
	 * 
	 * @param {string} eventName
	 */
	emit(eventName, data) {
		if ('string' === typeof eventName) {
			const queue = this._queue[eventName] || [];
			const fakeEventModel = EventEmiter.emitEventListener; // 伪造事件模型

			for (const actual of queue) {
				actual.data = data ? data : actual.data;
				const fakedEvent = fakeEventModel(actual.refer, actual.eventName, true);

				actual(fakedEvent);
			}
		}

		return this;
	};
}

/**
 * 全局基础配置
 * 所有 EventEmitter 实例的共享
 * 
 * @default defaultMaxListeners 内置最大监听数量，超过这个数量会抛出一个警告 
 * @default addEventListener DOM原生的事件注册函数 
 * @default removeEventListener DOM原生的事件销毁函数
 * @default emitEventListener DOM原生的事件自动触发与伪造事件模型函数
 **/
EventEmiter.defaultMaxListeners = 10;
EventEmiter.addEventListener = DefaultEventEmiter.addEventListener;
EventEmiter.removeEventListener = DefaultEventEmiter.removeEventListener;
EventEmiter.emitEventListener = DefaultEventEmiter.emitEventListener;